#include <strmif.h>
#include "cfactory.hpp"
#include "comreg.hpp"
#include <cassert>
#include <comdef.h>
#include <uuids.h>

HMODULE g_hModule;
static ULONG s_cLock;

_COM_SMARTPTR_TYPEDEF(IFilterMapper2, __uuidof(IFilterMapper2));    

namespace MkvMux
{
    extern const CLSID CLSID_MkvMux;

    HRESULT CreateInstance(
            IClassFactory*,
            IUnknown*, 
            const IID&, 
            void**);

}  //end namespace MkvMux


static MJH::CFactory s_factory(&s_cLock, &MkvMux::CreateInstance);


BOOL APIENTRY DllMain(
    HINSTANCE hModule, 
    DWORD  dwReason, 
    LPVOID)
{
	switch (dwReason)
	{
	    case DLL_PROCESS_ATTACH:
        {
            g_hModule = hModule;
	        break;
        }
	    case DLL_THREAD_ATTACH:
	    case DLL_THREAD_DETACH:
	    case DLL_PROCESS_DETACH:
	    default:
		    break;
	}
	
    return TRUE;
}



STDAPI DllCanUnloadNow()
{
    return s_cLock ? S_FALSE : S_OK;
}


STDAPI DllGetClassObject(
    const CLSID& clsid,
    const IID& iid,
    void** ppv)
{
    if (clsid == MkvMux::CLSID_MkvMux)
        return s_factory.QueryInterface(iid, ppv);

    return CLASS_E_CLASSNOTAVAILABLE;
}


STDAPI DllUnregisterServer()
{
    const IFilterMapper2Ptr pMapper(CLSID_FilterMapper2);
    assert(bool(pMapper));

    HRESULT hr = pMapper->UnregisterFilter(
                    &CLSID_LegacyAmFilterCategory,
                    0,
                    MkvMux::CLSID_MkvMux);
                    
    //assert(SUCCEEDED(hr));

    hr = ComReg::UnRegisterCoclass(MkvMux::CLSID_MkvMux);

    return SUCCEEDED(hr) ? S_OK : S_FALSE;
}


STDAPI DllRegisterServer()
{
    std::wstring filename_;

    HRESULT hr = ComReg::ComRegGetModuleFileName(g_hModule, filename_);
    assert(SUCCEEDED(hr));
    assert(!filename_.empty());
    
    const wchar_t* const filename = filename_.c_str();

#if _DEBUG    
    const wchar_t friendlyname[] = L"MJH Matroska Video (MKV) Muxer Filter (Debug Build)";
#else
    const wchar_t friendlyname[] = L"MJH Matroska Video (MKV) Muxer Filter";
#endif

    hr = DllUnregisterServer();
    assert(SUCCEEDED(hr));
    
    hr = ComReg::RegisterCoclass(
            MkvMux::CLSID_MkvMux,
            friendlyname,
            filename,
            L"MJH.MkvMux",
            L"MJH.MkvMux.1",
            false,  //not insertable
            false,  //not a control
            ComReg::kBoth,  //DShow filters must support "both"
            GUID_NULL,     //typelib
            0,    //no version specified
            0);   //no toolbox bitmap
            
    //hr = ComReg::RegisterTypeLibResource(filename, 0);
    //assert(SUCCEEDED(hr));

    const IFilterMapper2Ptr pMapper(CLSID_FilterMapper2);
    assert(bool(pMapper));

    REGFILTERPINS pins[3];
    
    //video inpin
    
    REGFILTERPINS& inpin_v = pins[0];
    
    inpin_v.strName = 0;              //obsolete
    inpin_v.bRendered = TRUE;  //TODO: correct value?
    inpin_v.bOutput = FALSE;
    inpin_v.bZero = FALSE;
    inpin_v.bMany = FALSE;
    inpin_v.clsConnectsToFilter = 0;  //obsolete
    inpin_v.strConnectsToPin = 0;     //obsolete
    
    enum { kVideoTypes = 1 };  //TODO

    const REGPINTYPES inpintypes_v[kVideoTypes] =
    {
        { &MEDIATYPE_Video, &MEDIASUBTYPE_MPEG2_VIDEO }
        //TODO: support more types
    };

    inpin_v.nMediaTypes = kVideoTypes;          
    inpin_v.lpMediaType = inpintypes_v;

    //audio inpin

    REGFILTERPINS& inpin_a = pins[1];

    inpin_a.strName = 0;
    inpin_a.bRendered = TRUE;  //TODO: correct value?
    inpin_a.bOutput = FALSE;
    inpin_a.bZero = FALSE;
    inpin_a.bMany = FALSE;
    inpin_a.clsConnectsToFilter = 0;
    inpin_a.strConnectsToPin = 0;
    
    enum { kAudioTypes = 1 };
    
    const REGPINTYPES inpintypes_a[kAudioTypes] =
    {
        { &MEDIATYPE_Audio, &MEDIASUBTYPE_DOLBY_AC3 }  //TODO: more subtypes
    };
    
    inpin_a.nMediaTypes = kAudioTypes;
    inpin_a.lpMediaType = inpintypes_a;

    //stream outpin
    
    REGFILTERPINS& outpin = pins[2];
    
    outpin.strName = 0;              //obsolete
    outpin.bRendered = FALSE;
    outpin.bOutput = TRUE;
    outpin.bZero = FALSE;
    outpin.bMany = FALSE;
    outpin.clsConnectsToFilter = 0;  //obsolete
    outpin.strConnectsToPin = 0;     //obsolete
    
    enum { kOutpinTypes = 1 };

    const REGPINTYPES outpin_types[kOutpinTypes] =
    {
        { &MEDIATYPE_Stream, &GUID_NULL }  //TODO: need mkv media subtype guid
    };
    
    outpin.nMediaTypes = kOutpinTypes;          
    outpin.lpMediaType = outpin_types;
    
    //pin setup complete
    
    REGFILTER2 filter;
    
    filter.dwVersion = 1;
    filter.dwMerit = MERIT_DO_NOT_USE;
    filter.cPins = 3;
    filter.rgPins = pins;
    
    hr = pMapper->RegisterFilter(
            MkvMux::CLSID_MkvMux,
            friendlyname,
            0,
            &CLSID_LegacyAmFilterCategory,
            0,
            &filter);                       

    return hr;
}
